﻿using CodePlex.TfsPluginSuite.Configuration;
using CodePlex.TfsPluginSuite.Configuration.ChangeTypePolicy;
using CodePlex.TfsPluginSuite.Helper;
using Microsoft.TeamFoundation.Common;
using Microsoft.TeamFoundation.Framework.Server;
using Microsoft.TeamFoundation.VersionControl.Server;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace CodePlex.TfsPluginSuite.Policies
{
    public class ChangeTypePolicy : ISubscriber
    {
        /// <summary>
        /// dictionary where the key is a regex and the value is the set of permitted changetypes 
        /// </summary>
        private static Dictionary<string, List<ChangeType>> allowedChangeTypes;

        public string Name
        {
            get { return typeof(ChangeTypePolicy).FullName; }
        }

        public SubscriberPriority Priority
        {
            get { return SubscriberPriority.Normal; }
        }

        //When debuging, copy .dlls and .pdb 
        //to %Program Files%\Microsoft Team Foundation Server 2010\Application Tier\Web Services\bin\Plugins
        public EventNotificationStatus ProcessEvent(IVssRequestContext requestContext,
            NotificationType notificationType,
            object notificationEventArgs,
            out int statusCode,
            out string statusMessage,
            out ExceptionPropertyCollection properties)
        {
            EventNotificationStatus response = EventNotificationStatus.ActionPermitted;
            properties = new ExceptionPropertyCollection();
            statusMessage = String.Empty;
            statusCode = 0;

            LoadChangeTypes();

            try
            {
                // If this is a Check-in notification and is a decision point and is not overriden
                if (notificationEventArgs is CheckinNotification
                    && notificationType == NotificationType.DecisionPoint
                    && TfsPluginSuiteConfigurationManager.Section.ChangeTypePolicy.Enabled)
                {
                    CheckinNotification notification = notificationEventArgs as CheckinNotification;

                    if (notification.CheckinType == CheckinType.Workspace)
                    {
                        using (var tfsHelper = new TfsHelper(requestContext, notification))
                        {
                            foreach (var change in tfsHelper.GetPendingChanges())
                            {
                                var attemptedChangeType = (ChangeType)change.ChangeEx;

                                foreach (var folder in allowedChangeTypes)
                                {
                                    //if in the configured folder and there are not any allowed changes in the attempted changes, then barf
                                    if (Regex.IsMatch(change.ServerItem, folder.Key)
                                        && !folder.Value.Any(type => attemptedChangeType.HasFlag(type)))
                                    {
                                        statusMessage = string.Format("{0} is not allowed in {1}", attemptedChangeType.ToString(), change.ServerItem);
                                        properties.Set("item path", change.ServerItem);
                                        return EventNotificationStatus.ActionDenied; //can only handle one error at a time with server side policies
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                statusCode = -1;
                statusMessage += e.ToString();
                requestContext.Status = e;
                response = EventNotificationStatus.ActionDenied;
            }
            return response;
        }

        public Type[] SubscribedTypes()
        {
            return new Type[] { typeof(CheckinNotification) };
        }

        /// <summary>
        /// we lazy load the permitted change types to a cache because there will be a permformance hit as these are evaluated against all changeset check-ins
        /// </summary>
        private static void LoadChangeTypes()
        {
            if (allowedChangeTypes == null)
            {
                allowedChangeTypes = new Dictionary<string, List<ChangeType>>();
                foreach (FolderConfigurationElement folder in TfsPluginSuiteConfigurationManager.Section.ChangeTypePolicy)
                {
                    var types = new List<ChangeType>();
                    foreach (AllowedChangeTypeConfigurationElement allowedType in folder)
                    {
                        ChangeType type;
                        Enum.TryParse<ChangeType>(allowedType.Name, out type);
                        types.Add(type);
                    }
                    allowedChangeTypes.Add(folder.Regex, types);
                }
            }
        }
    }
}
